#include "HTTPRequest.h"


map<string,string> MimeTypes::types;

HTTPRequest::HTTPRequest(char* _request,int _length)
{
	requestLength=_length;
	requestBuffer=(char*)malloc(requestLength);
	memcpy(requestBuffer,_request,requestLength);
}
HTTPRequest::HTTPRequest(const HTTPRequest& src)
{
	requestLength=src.requestLength;
	requestBuffer=(char*)malloc(requestLength);
	memcpy(requestBuffer,src.requestBuffer,requestLength);
}
HTTPRequest& HTTPRequest::operator=(const HTTPRequest& src)
{
	int length=src.requestLength;
	char* buffer=(char*)malloc(length);
	memcpy(requestBuffer,src.requestBuffer,requestLength);

	delete requestBuffer;
	requestBuffer=buffer;
	requestLength=length;
	return *this;
}
HTTPRequest::~HTTPRequest()
{
	delete requestBuffer;
}
void HTTPRequest::GetResponse(char** response,int* responseLength)
{
	if(!ParseRequest())
	{
		WriteResponse("500 Server Error",response,responseLength);
		return;
	}

	if(path=="\\")
		path="\\index.html";

	string source=GetWebRootPath()+path;
	ifstream fileStream(source.c_str(),ios::binary);
	if(!fileStream.is_open())
	{
		fileStream.close();
		WriteResponse("404 Not Found",response,responseLength);
		return;
	}

	fileStream.seekg(0,ios::end);
	int fileLength=(int)fileStream.tellg();

	string responseHeader="HTTP/1.0 200 OK\n";
	responseHeader+="Content-Type: "+MimeTypes::GetType(GetExtension(source))+"\n";
	responseHeader+="Content-Length: "+ToString(fileLength)+"\n";
	responseHeader+="\n";
	int responseHeaderLength=responseHeader.size();
	*responseLength=responseHeaderLength+fileLength;
	*response=(char*)malloc(*responseLength*sizeof(char));
	memset(*response,0,*responseLength*sizeof(char));
	memcpy(*response,responseHeader.c_str(),responseHeaderLength);

	fileStream.seekg(0,ios_base::beg);
	fileStream.read(*response+responseHeaderLength,fileLength);
	fileStream.close();

}
bool HTTPRequest::ParseRequest()
{
	type="";
	path="";
	protocol="";
	headers.clear();
	content="";

	vector<string> lines;
	Tokenize(requestBuffer,lines,"\r\n");
	if(lines.size()==0)
		return false;
	
	//parse first line
	vector<string> initialLineTokens;
	Tokenize(lines.at(0),initialLineTokens," ");
	if(initialLineTokens.size()!=3)
		return false;
	if(initialLineTokens.at(0)!="GET") //currently, there's no support for anything other than a GET request
		return false;
	type=initialLineTokens.at(0);
	path=SanitizePath(initialLineTokens.at(1));
	protocol=initialLineTokens.at(2);

	//parse headers
	for(size_t i=1;i<lines.size();i++)
	{
		string line=lines.at(i);
		int pos=line.find(": ");
		string key=line.substr(0,pos);
		string value=line.substr(pos+1);
		headers[key]=value;
	}
	return true;
}
void HTTPRequest::Tokenize(const string& str,vector<string>& tokens,const string& delimiters)
{
	tokens.clear();
    string::size_type lastPos=str.find_first_not_of(delimiters,0);
    string::size_type pos=str.find_first_of(delimiters,lastPos);
    while(string::npos!=pos || string::npos!=lastPos)
    {
        tokens.push_back(str.substr(lastPos, pos - lastPos));
        lastPos = str.find_first_not_of(delimiters, pos);
        pos = str.find_first_of(delimiters, lastPos);
    }
}
string HTTPRequest::GetExtension(const string& path)
{
	return path.substr(path.rfind(".")+1,string::npos);
}
string HTTPRequest::GetHeaderValue(const string& key)
{
	if(headers.find(key)==headers.end())
		return "";
	return headers[key];
}

string HTTPRequest::GetWebRootPath()
{
	char buffer[MAX_PATH];
	GetModuleFileName(NULL,buffer,MAX_PATH);
	string ret=buffer;
	ret=ret.erase(ret.rfind('\\'));
	ret=ret.erase(ret.rfind('\\')+1);
	ret+="webroot";
	return ret;
}

string HTTPRequest::SanitizePath(const string& path)
{
	string ret=path;
	while(true)
	{
		string::size_type pos=ret.find("/../");
		if(pos==string::npos)
			break;
		ret.replace(pos,4,"/");
	}
	while(true)
	{
		string::size_type pos=ret.find("/./");
		if(pos==string::npos)
			break;
		ret.replace(pos,3,"/");
	}
	while(true)
	{
		string::size_type pos=ret.find("/");
		if(pos==string::npos)
			break;
		ret.replace(pos,1,"\\");
	}
	return ret;
}
string HTTPRequest::ToString(int num)
{
	char buffer[50];
	_itoa_s(num,buffer,10);
	return buffer;
}
void HTTPRequest::WriteResponse(const string& response,char** buffer,int* length)
{
	int len=response.size();

	*length=(len+1)*sizeof(char);
	*buffer=(char*)malloc((len+1)*sizeof(char));
	memset(*buffer,0,*length);
	memcpy(*buffer,response.c_str(),len*sizeof(char));
}